#include "drawerADT.h"
#include "semaphoreADT.h"
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>

struct semaphoreCDT{
	int x;
	int y;
	int duration;
	pthread_mutex_t mutexVer;
	pthread_cond_t condVer;
	int semVerColor;
	drawerADT drawer;
} semaphoreCDT;

static void changeColor(semaphoreADT semaphore);
static void stopSemaphore(semaphoreADT semaphore);

semaphoreADT
newSemaphore(int secs, int x, int y, drawerADT drawer)
{
	semaphoreADT semaphore;
	
	if((semaphore=malloc(sizeof(struct semaphoreCDT)))==NULL)
		return NULL;
		
	semaphore->x=x;
	semaphore->y=y;
	semaphore->duration= secs;
	
	pthread_mutex_init(&semaphore->mutexVer, NULL);
	pthread_cond_init(&semaphore->condVer, NULL);
	semaphore->semVerColor= SEM_GREEN;
	semaphore->drawer = drawer;
		
	return semaphore;
}

void
freeSemaphore(semaphoreADT semaphore)
{
	stopSemaphore(semaphore);
	pthread_cond_destroy(&semaphore->condVer);
	pthread_mutex_destroy(&semaphore->mutexVer);
	free(semaphore);
	return;
}

int
getSemX(semaphoreADT semaphore)
{
	return semaphore->x;
}

int
getSemY(semaphoreADT semaphore)
{
	return semaphore->y;
}

int 
getColor(semaphoreADT semaphore, int orientation)
{
	int color;
	
	if(orientation==SEM_VER)
	{
		color= semaphore->semVerColor;
	}
	else
	{
		if(semaphore->semVerColor==SEM_GREEN)
			color=SEM_RED;
		else
			color=SEM_GREEN;
	}
	return color;
}

void
changeDuration(semaphoreADT semaphore, int newDuration)
{
	if (semaphore->duration + newDuration <= 0)
		return;
		
	semaphore->duration += newDuration;
}

static void
stopSemaphore(semaphoreADT semaphore)
{
	pthread_mutex_trylock(&semaphore->mutexVer);
	pthread_cond_broadcast(&semaphore->condVer);
	pthread_mutex_unlock(&semaphore->mutexVer);
}

void *
startSemaphore(void * semaphore)
{
	semaphoreADT sem;
	
	sem = (semaphoreADT)semaphore;
	pthread_cleanup_push((void (*) (void *))stopSemaphore, sem);	
	drawSem(sem);

	while(1)
	{
		changeColor(sem);
		sleep(sem->duration);
	}
	pthread_cleanup_pop(0);
}

static void
changeColor(semaphoreADT semaphore)
{
	if(getColor(semaphore, SEM_VER)==SEM_RED)
	{
		pthread_mutex_lock(&semaphore->mutexVer);
		semaphore->semVerColor=SEM_GREEN;
		pthread_cond_broadcast(&semaphore->condVer);
		pthread_mutex_unlock(&semaphore->mutexVer);
		
	}
	else
	{ 
		pthread_mutex_lock(&semaphore->mutexVer);
		semaphore->semVerColor=SEM_RED;
		pthread_cond_broadcast(&semaphore->condVer);
		pthread_mutex_unlock(&semaphore->mutexVer);
	}

	drawSem(semaphore);
}

void
waitSemaphoreToChange(semaphoreADT semaphoreInRed, int orientation)
{
	struct timespec   ts;
  	struct timeval    tp;
 
  	gettimeofday(&tp, NULL); 	
  	ts.tv_sec  = tp.tv_sec;
   	ts.tv_nsec = tp.tv_usec * 1000;
   	ts.tv_sec += semaphoreInRed->duration;

	if(orientation == SEM_VER)
	{
		pthread_mutex_lock(&semaphoreInRed->mutexVer);
		if(getColor(semaphoreInRed, SEM_VER)==SEM_RED)
			pthread_cond_timedwait(&semaphoreInRed->condVer, &semaphoreInRed->mutexVer, &ts);
		pthread_mutex_unlock(&semaphoreInRed->mutexVer);
	}
	else
	{
		pthread_mutex_lock(&semaphoreInRed->mutexVer);
		if(getColor(semaphoreInRed, SEM_HOR)==SEM_RED)
			pthread_cond_timedwait(&semaphoreInRed->condVer, &semaphoreInRed->mutexVer, &ts);
		pthread_mutex_unlock(&semaphoreInRed->mutexVer);
	}
	return;
}

void
drawSem(semaphoreADT semaphore)
{
	if(semaphore!=NULL)
	{
		pthread_mutex_lock(&semaphore->mutexVer);
		setSemaphore(semaphore->drawer, SEM_HOR, (semaphore->semVerColor?GREEN:RED), semaphore->x, semaphore->y);
		setSemaphore(semaphore->drawer, SEM_HOR, (semaphore->semVerColor?GREEN:RED), semaphore->x + 1, semaphore->y + 1);
		setSemaphore(semaphore->drawer, SEM_VER, (semaphore->semVerColor?RED:GREEN), semaphore->x, semaphore->y + 1);
		setSemaphore(semaphore->drawer, SEM_VER, (semaphore->semVerColor?RED:GREEN), semaphore->x + 1, semaphore->y);
		draw(semaphore->drawer);
		pthread_mutex_unlock(&semaphore->mutexVer);
	}

}
